import struct
import sys
import serial
import os
import time
import socket
import json
import threading
import requests
import screenshot
import record

SOH = b'\x01'
EOT = b'\x04'
ACK = b'\x06'
NAK = b'\x15'
SYN = b'\x16'
CAN = b'\x18'

MyDeviceID = 0x1001
packet_num = 0
ser = None

CMD_RESERVE0 = 0
CMD_RESERVE1 = 1
CMD_PING = 2
CMD_STOREFILE = 3
CMD_LOADFILE = 4
CMD_UPDATEWEATHER = 5
CMD_SCREENSHOT = 6
CMD_PROCIMG = 7
CMD_STARTRECORD = 8
CMD_RECORD = 9
CMD_STOPRECORD = 10
CMD_MYIP = 11

finished = False
isReceiving = False
isSending = False
SYNReceived = False
ACKReceived = False

receivedData = b''
current_id = 0
pktQueue = []
last_packet = 0

exceptPath = os.path.join(os.path.expanduser(
    '~'), "Desktop") + r"\ExceptionHappened.txt"

serversocket = socket.socket(
    socket.AF_INET, socket.SOCK_STREAM)


def tryToConnectSerial():
    global ser
    print("尝试重新连接串口")
    f = open(exceptPath, "w")
    f.write("串口设备无法连接")
    f.close()
    while True:
        if sys.platform == 'linux':
            for i in range(0, 5):
                try:
                    ser = serial.Serial(
                        "/dev/ttyUSB" + str(i), timeout=0.02, baudrate=1000000)
                    ser.read_all()
                    ser.write(b'C')
                    time.sleep(0.3)
                    if(ser.read() == b'T'):
                        os.remove(exceptPath)
                        return
                except serial.SerialException:
                    pass
        else:
            for i in range(1, 10):
                try:
                    print("COM" + str(i))
                    ser = serial.Serial(
                        "COM" + str(i), timeout=0.02, baudrate=1000000)
                    ser.read_all()
                    ser.write(b'PING;')
                    time.sleep(0.2)
                    if(ser.readline() == b'ACK;\n'):
                        ser.write("ROLE MASTER;INIT;".encode(encoding='ascii'))
                        time.sleep(0.3)
                        ser.read_all()
                        os.remove(exceptPath)
                        return
                    ser.close()
                except serial.SerialException:
                    if ser != None:
                        if ser.isOpen():
                            ser.close()
        time.sleep(60)


def bytesToArray(bs):
    res = "["
    for c in bs:
        s = hex(c)
        res += s[2:]
        res += ","
    res = res[:-1] + "]"
    return res


def sendTo(deviceID, payload):
    global ACKReceived
    ACKReceived = False
    for i in range(10):
        packet = struct.pack("<HH" + str(len(payload)) +
                             's', MyDeviceID, deviceID, payload)
        cmd = "REQ 3 " + bytesToArray(packet) + ";"
        ser.write(cmd.encode(encoding='ascii'))
        # print(cmd)
        trycount = 0
        if(SYNReceived == True):
            return False
        while ACKReceived == False:
            trycount += 1
            if(trycount == 6):
                break
            time.sleep(0.01)
        if(ACKReceived == True):
            return True
    return False


def reply(packetNum, deviceID, payload):
    packet = struct.pack("<HHs", MyDeviceID, deviceID, payload)
    cmd = "REPLY 3 " + packetNum + " " + bytesToArray(packet) + ";"
    #print("[REPLY] ", cmd)
    ser.write(cmd.encode(encoding='ascii'))

# 返回值说明：(发送者，接收者, 数据)


def unPack(packet):
    header = packet[:4]
    header = struct.unpack("<HH", header)
    return (header[0], header[1], packet[4:])


def unPack2(packet):
    header = packet[:4]
    id = struct.unpack("<I", header)[0]
    return (id, packet[4:])


def Pack2(id, packet):
    header = struct.pack("<I", id)
    return header + packet


def serve_inQueue(sender, payload):
    global pktQueue
    pktQueue.append((sender, payload))
    pass


def paraseSerial():
    global isReceiving, isSending, current_id, finished, SYNReceived, ACKReceived, receivedData, last_packet
    line = ser.readline()
    if(line == b""):
        return
    line = line.decode()
    line = line.split(' ')
    # print(line)
    if(line[0] == 'REC'):
        rescode = line[1]
        line[2] = line[2].replace(";", "").replace(
            ",", ",0x").replace("[", "[0x")
        payload = bytes(eval(line[2]))
        if(len(payload) < 4):
            return
        sender, receiver, payload = unPack(payload)
        if(receiver != MyDeviceID):
            return
        if(payload == ACK):
            ACKReceived = True
            return
        reply(rescode, sender, ACK)
        if(isReceiving == False) and (isSending == False):
            serve_inQueue(sender, payload)
        else:
            id, data = unPack2(payload)
            last_packet = 0  # “看门狗”
            # 解包完成，开始处理数据
            if(isReceiving):
                if(current_id >= id):
                    # 收到一个之前的包
                    return
                else:
                    receivedData += data
                    if(len(data) == 0):
                        finished = True
                        isReceiving = False
                        current_id = 0
                        return
                    current_id += 1
            elif(isSending):
                if(id == 0) and data == SYN:
                    SYNReceived = True
                    current_id = 1


def receiveFile(target, filename):
    global receivedData, isReceiving, finished, current_id, last_packet, SYNReceived
    receivedData = b''
    finished = False
    isReceiving = True
    SYNReceived = False
    current_id = 0
    last_packet = 0
    if(sendTo(target, Pack2(0, SYN))):
        # print("SYNSent")
        while finished == False:
            time.sleep(0.2)
            print(len(receivedData))
            last_packet += 1
            if(last_packet > 15):
                break
        isReceiving = False
        if(finished == False):
            return False
        f = open(filename, "wb")
        f.write(receivedData)
        f.close()
        return True


def sendFile(target, filename):
    global isSending, SYNReceived, ACKReceived, current_id
    f = open(filename, "rb")
    isSending = True
    SYNReceived = False
    try_count = 0
    while SYNReceived == False:
        time.sleep(0.1)
        try_count += 1
        if(try_count >= 30):
            isSending = False
            return False
    SYNReceived = False
    #print("SYN Received!")
    d = b''
    total = 0
    while True:
        print(total)
        d = f.read(64)
        l = len(d)
        p = Pack2(current_id, d)
        if sendTo(target, p) == False:
            print("Connection lost!")
            isSending = False
            return False
        if(l == 0):
            break
        current_id += 1
        total += l
    isSending = False
    return True


def getfilename(data):
    if(data[-1] == 0):
        data = data[:-1]
    filename = bytes(data).decode(encoding='ascii')
    filename = "D:\\data\\" + filename
    return filename


def serve():
    if(len(pktQueue)):
        group = pktQueue.pop(0)
        if(len(group[1]) == 0):
            return
        command = group[1][0]
        data = group[1][1:]
        if command == CMD_PING:
            return
        if command == CMD_STOREFILE:
            filename = getfilename(data)
            print("Storing File:", filename)
            time.sleep(0.6)
            receiveFile(group[0], filename)
            print("Done.")
        elif command == CMD_LOADFILE:
            filename = getfilename(data)
            print("Sending File:", filename)
            sendFile(group[0], filename)
            print("Done.")
        elif command == CMD_SCREENSHOT:
            filename = getfilename(data)
            screenshot.grab(filename)
        elif command == CMD_PROCIMG:
            filename = getfilename(data)
            filename, method = filename.split(' ')
            screenshot.procImageBackground(filename, method)
        elif command == CMD_STARTRECORD:
            record.start_record()
        elif command == CMD_RECORD:
            record.save()
        elif command == CMD_STOPRECORD:
            record.stop_record()
        elif command == CMD_MYIP:
            myip = socket.gethostbyname(socket.gethostname())
            sendTo(group[0], myip.encode("ascii"))


def server_run():
    while(True):
        try:
            serve()
            time.sleep(0.05)
        except Exception as e:
            print(e)
            print(e.__traceback__)


def thread_parase():
    while True:
        try:
            paraseSerial()
        except:
            tryToConnectSerial()


def thread_tcp_server():
    global messages
    while True:
        try:
            clientsocket, addr = serversocket.accept()
            while True:
                # 处理数据
                clientsocket.settimeout(10)
                recv = clientsocket.recv(1024).decode("ascii")
                if(recv == b""):
                    break
                send = {
                    "method": "ok",
                    "payload": ""
                }
                recvjson = json.loads(recv)

                if(recvjson["method"] == "get"):
                    send["method"] = "response"
                    send["payload"] = "No messages"

                elif(recvjson["method"] == "send"):
                    if(recvjson["payload"] != ""):
                        send["method"] = "response"
                        send["payload"] = "sent"

                elif(recvjson["method"] == "count"):
                    send["method"] = "response"
                    send["payload"] = 0

                elif(recvjson["method"] == "clear"):
                    send["method"] = "response"
                    send["payload"] = True

                elif(recvjson["method"] == "cmd"):
                    send["method"] = "response"
                    send["payload"] = os.system(recvjson["payload"])

                elif(recvjson["method"] == "music"):
                    send["method"] = "response"
                    send["payload"] = ""
                    r = requests.Session()
                    res = r.get(
                        "https://api.uomg.com/api/rand.music?sort=热歌榜&format=json")
                    if(res.status_code == 200):
                        j = json.loads(res.text)
                        k = {
                            "name": j["data"]["name"],
                            "url": r.get(j["data"]["url"]).url
                        }
                        send["payload"] = json.dumps(k)

                msg = json.dumps(send)
                msg += '\n'
                clientsocket.send(msg.encode('utf-8'))
        except Exception as e:
            print(e)
            print(repr(e))
            print(recv)
            try:
                clientsocket.close()
            except:
                pass


def startTCP():
    serversocket.bind((socket.gethostbyname(socket.gethostname()), 20054))
    serversocket.listen(50)
    thread_tcpserver = threading.Thread(target=thread_tcp_server, daemon=True)
    thread_tcpserver.start()
